/***
* Feel free to use this code however you wish.
* Created by Alie Tan (www(dot)alietan(dot)com)
*/

#include <ma.h>
#include <mastdlib.h>

#include <MoBox2D/Box2D.h>

#include "Sprite.h"
#include "MAHeaders.h"

int gScreenWidth;
int gScreenHeight;

b2World *gWorld;

CSprite *ballSprite;
CSprite *paddleSprite;

#define SCALE 1.0

// Ahhh!, nothing like dangerous, multi-valued stuff...  But it's handy!
#define SCREEN_TO_PHYS(x, y) (x)/SCALE, (y)/SCALE
#define PHYS_TO_SCREEN(x, y) (x)*SCALE, (y)*SCALE


void loadBallTexture()
{
	{
	MAExtent textureSize = maGetImageSize(R_BALL);
	int width = EXTENT_X(textureSize);
	int height = EXTENT_Y(textureSize);
	unsigned int *texture = new unsigned int [width*height];
	MARect srcRect = {0, 0, width, height};
	maGetImageData(R_BALL, texture, &srcRect, width);
	ballSprite = new CSprite(texture, width, height);
	}

	{
	MAExtent textureSize = maGetImageSize(R_PADDLE);
	int width = EXTENT_X(textureSize);
	int height = EXTENT_Y(textureSize);
	unsigned int *texture = new unsigned int [width*height];
	MARect srcRect = {0, 0, width, height};
	maGetImageData(R_PADDLE, texture, &srcRect, width);
	paddleSprite = new CSprite(texture, width, height);
	}
}

void initBalls()
{
	loadBallTexture();

	// paddle
	b2BodyDef bodyDef;
	bodyDef.position.Set(gScreenWidth/2, gScreenHeight-100);
	bodyDef.userData = paddleSprite;
	b2Body *paddleBody = gWorld->CreateBody(&bodyDef);

	b2PolygonShape polygonShape;
	polygonShape.SetAsBox(60.0f, 20.0f);

	b2FixtureDef fixtureDef;
	fixtureDef.shape = &polygonShape;
	fixtureDef.density = 1.0f;
	fixtureDef.friction = 0.2f;
	fixtureDef.restitution = 0.2f;
	paddleBody->CreateFixture(&fixtureDef);

	srand(maLocalTime());
	for(int i = 0; i < 3; i++)
	{
		// Create ball body and shape
		b2BodyDef ballBodyDef;
		ballBodyDef.type = b2_dynamicBody;
		int x = (gScreenWidth-100)&rand();
		int y = (gScreenHeight-100)&rand();
		ballBodyDef.position.Set(50+x, 50+y);

		ballBodyDef.userData = ballSprite;

		b2CircleShape circle;
		circle.m_radius = (ballSprite->getWidth()/2)/SCALE;

		b2FixtureDef ballShapeDef;
		ballShapeDef.shape = &circle;
		ballShapeDef.density = 100.0f;
		ballShapeDef.friction = 0.4f;
		ballShapeDef.restitution = 0.8f;

		b2Body *body = gWorld->CreateBody(&ballBodyDef);
		body->CreateFixture(&ballShapeDef);
	}
}

void initWorld()
{
	// Create a world
	b2Vec2 gravity = b2Vec2(0.0f, 60.0f);
	bool doSleep = true;
	gWorld = new b2World(gravity, doSleep);

	// Create edges around the entire screen
	b2BodyDef groundBodyDef;
	b2Body *groundBody = gWorld->CreateBody(&groundBodyDef);
	b2PolygonShape groundBox;
	b2FixtureDef boxShapeDef;
	boxShapeDef.shape = &groundBox;
	groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(SCREEN_TO_PHYS(gScreenWidth, 0)));
	groundBody->CreateFixture(&boxShapeDef);
	groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(SCREEN_TO_PHYS(0, gScreenHeight)));
	groundBody->CreateFixture(&boxShapeDef);
	groundBox.SetAsEdge(b2Vec2(SCREEN_TO_PHYS(0, gScreenHeight)), b2Vec2(SCREEN_TO_PHYS(gScreenWidth, gScreenHeight)));
	groundBody->CreateFixture(&boxShapeDef);
	groundBox.SetAsEdge(b2Vec2(SCREEN_TO_PHYS(gScreenWidth, gScreenHeight)), b2Vec2(SCREEN_TO_PHYS(gScreenWidth, 0)));
	groundBody->CreateFixture(&boxShapeDef);
}

extern "C" int MAMain()
{
	/// Get screen dimensions.
	MAExtent size = maGetScrSize();
	/// Extract the screen width
	gScreenWidth = EXTENT_X(size);
	/// Extract the screen height
	gScreenHeight = EXTENT_Y(size);

	/// Create the image in which the cube will be drawn, to draw multiple versions of it.
	maCreateDrawableImage(1, gScreenWidth, gScreenHeight);

	initWorld();
	initBalls();

    // Now sim code
    float32 timeStep = 1.0f / 60.0f;

    bool quit = false;
    while(!quit)
    {
    	/// Set the following draw calls to draw to the drawable image created (draw target 1).
    	maSetDrawTarget(1);

    	maSetColor(0x0088ff);
		maFillRect(0, 0, gScreenWidth, gScreenHeight);

    	gWorld->Step(timeStep, 1, 1);
		for(b2Body *b = gWorld->GetBodyList(); b; b=b->GetNext())
		{
			if (b->GetUserData() != NULL)
			{
				CSprite *sprite = (CSprite *)b->GetUserData();
				int width = sprite->getWidth();
				int height = sprite->getHeight();
				int x = (int)(b->GetPosition().x*SCALE - width/2);
				int y = (int)(b->GetPosition().y*SCALE - height/2);
				float32 angle = b->GetAngle();
				void *texture = sprite->getData();

				MARect srcRect = {0, 0, width, height};
				MAPoint2d dstPoint = {x, y};
				maDrawRGB(&dstPoint, texture, &srcRect, width);
			}
		}

		// Set the following draw calls to draw to the screen (draw target 0).
		maSetDrawTarget(0);

		MARect srcRect = {0, 0, gScreenWidth, gScreenHeight};
		MAPoint2d dstPoint = {0, 0};
		maDrawImageRegion(1, &srcRect, &dstPoint, TRANS_NONE);

		/// Updates the screen
		maUpdateScreen();

		/// Keep the backlight alive.
		maResetBacklight();

		MAEvent event;

		while(maGetEvent(&event)) {
			switch(event.type) {
			// Make sure we catch the close event and if we do, exit.
			case EVENT_TYPE_CLOSE:
				quit = true;
			break;
			// Whenever the screen area or orientation of the screen changes, we reinitialize the FrameBuffer library.
			case EVENT_TYPE_SCREEN_CHANGED:
			break;
			}
		}
    }

    delete gWorld;
    delete ballSprite;
    delete paddleSprite;
	return 0;
}
